#!/usr/bin/env bash
set -e

status=127
program="${0##*/}"
PROGRAM="$(echo "$program" | tr a-z- A-Z_)"
[ -n "$TMPDIR" ] || TMPDIR="/tmp"

_STUB_PLAN="${PROGRAM}_STUB_PLAN"
_STUB_RUN="${PROGRAM}_STUB_RUN"
_STUB_INDEX="${PROGRAM}_STUB_INDEX"
_STUB_NOINDEX="${PROGRAM}_STUB_NOINDEX"
_STUB_RESULT="${PROGRAM}_STUB_RESULT"
_STUB_END="${PROGRAM}_STUB_END"
_STUB_DEBUG="${PROGRAM}_STUB_DEBUG"

if [ -n "${!_STUB_DEBUG}" ] && [ -z "${!_STUB_END}" ]; then
  echo stub: "$program" "$@" >&${!_STUB_DEBUG}
fi

[ -e "${!_STUB_PLAN}" ] || exit 1
[ -n "${!_STUB_RUN}" ] || eval "${_STUB_RUN}"="${TMPDIR}/${program}-stub-run"


# Initialize or load the stub run information.
eval "${_STUB_INDEX}"=1
eval "${_STUB_RESULT}"=0
# shellcheck disable=SC1090
[ ! -e "${!_STUB_RUN}" ] || source "${!_STUB_RUN}"

# Expose this for stub scripts.
# shellcheck disable=SC2317
inspect_args() {
  local arg
  local sep=''
  for arg; do
    if [[ $arg == *' '* ]]; then
      printf '%s"%s"' "$sep" "${arg//\"/\\\"}"
    elif [[ $arg == *'"'* ]]; then
      printf "%s'%s'" "$sep" "$arg"
    else
      printf '%s%s' "$sep" "$arg"
    fi
    sep=" "
  done
}

# Loop over each line in the plan.
IFS=$'\n' read -d '' -r -a lines < "${!_STUB_PLAN}" || true
index=0
for line in "${lines[@]}"; do
  index=$(($index + 1))

  if [[ -z "${!_STUB_END}" && -n "${!_STUB_NOINDEX}" || $index -eq "${!_STUB_INDEX}" ]]; then
    # We found the plan line we're interested in.
    # Start off by assuming success.
    result=0

    # Split the line into an array of arguments to
    # match and a command to run to produce output.
    command=" $line"
    if [ "$command" != "${command/ : }" ]; then
      patterns="${command%% : *}"
      command="${command#* : }"
    fi

    # Naively split patterns by whitespace for now.
    # In the future, use a sed script to split while
    # respecting quoting.
    set -f
    patterns=($patterns)
    set +f
    arguments=("$@")

    # Match the expected argument patterns to actual
    # arguments.
    for (( i=0; i<${#patterns[@]}; i++ )); do
      pattern="${patterns[$i]}"
      argument="${arguments[$i]}"

      case "$argument" in
        $pattern ) ;;
        * ) result=1 ;;
      esac
    done

    # If the arguments matched, evaluate the command
    # in a subshell. Otherwise, log the failure.
    if [ $result -eq 0 ] ; then
      set +e
      ( eval "$command" )
      status="$?"
      set -e
      [ -z "${!_STUB_NOINDEX}" ] || break
    elif [ -z "${!_STUB_NOINDEX}" ]; then
      eval "${_STUB_RESULT}"=1
    fi
  fi
done


if [ -n "${!_STUB_END}" ]; then
  # Clean up the run file.
  rm -f "${!_STUB_RUN}"

  stub_index_value="${!_STUB_INDEX}"
  # If the number of lines in the plan is larger than
  # the requested index, we failed.
  if [ "$index" -ge "$stub_index_value" ]; then
    echo "$program: expected $index invocations, got $((stub_index_value-1))" >&2
    eval "${_STUB_RESULT}"=1
  fi

  # Return the result.
  exit "${!_STUB_RESULT}"

else
  # If the requested index is larger than the number
  # of lines in the plan file, we failed.
  if [[ -z "${!_STUB_NOINDEX}" && "${!_STUB_INDEX}" -gt $index ]]; then
    eval "${_STUB_RESULT}"=1
  fi

  # Write out the run information.
  { echo "${_STUB_INDEX}=$((${!_STUB_INDEX} + 1))"
    echo "${_STUB_RESULT}=${!_STUB_RESULT}"
  } > "${!_STUB_RUN}"

  exit "$status"

fi
